package com.xzqwjw.taskmanager.utility;

import java.util.ArrayList;
import java.util.List;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public final class SQLiteHelper extends SQLiteOpenHelper {

  private static final String TAG = "SQLiteHelper";
  private static SQLiteHelper INSTANCE;
  private final ICallBack itfCallBack;

  private SQLiteHelper(Context objContext, ICallBack itfCallBack) {
    super(objContext, itfCallBack.getDatabaseName(), null, itfCallBack.getVersion());
    this.itfCallBack = itfCallBack;
  }

  public static void Initialize(@NonNull Context objContext, @NonNull ICallBack itfCallBack) {
    INSTANCE = new SQLiteHelper(objContext, itfCallBack);
  }

  @Override
  public void onCreate(SQLiteDatabase objSQLiteDatabase) {
    List<String> listCreateTablesSQLString = itfCallBack.createTablesSQLString();
    for (String strCreateTablesSQLString : listCreateTablesSQLString) {
      objSQLiteDatabase.execSQL(strCreateTablesSQLString);
      Log.d(TAG, "CREATE TABLE " + "[ \n" + strCreateTablesSQLString + "\n ]" + " successful! ");
    }
  }

  @Override
  public void onUpgrade(SQLiteDatabase objSQLiteDatabase, int intVersionOld, int intVersionNew) {
    itfCallBack.onUpgrade(objSQLiteDatabase, intVersionOld, intVersionNew);
  }

  public interface ICallBack {
    String getDatabaseName();

    int getVersion();

    void onUpgrade(SQLiteDatabase db, int intVersionOld, int intVersionNew);

    List<String> createTablesSQLString();

    <T> void assignValuesByEntity(String strTableName, T objEntity, ContentValues objContentValues);

    <T> T newEntityByCursor(String strTableName, Cursor objCursor);
  }

  public static <T> void insert(String strTableName, T objEntity) {
    SQLiteDatabase objSQLiteDatabase = INSTANCE.getWritableDatabase();
    objSQLiteDatabase.beginTransaction();
    try {
      ContentValues objContentValues = new ContentValues();
      INSTANCE.itfCallBack.assignValuesByEntity(strTableName, objEntity, objContentValues);
      objSQLiteDatabase.insert(strTableName, null, objContentValues);
      objContentValues.clear();
      objSQLiteDatabase.setTransactionSuccessful();
    } finally {
      objSQLiteDatabase.endTransaction();
      objSQLiteDatabase.close();
    }
  }

  public static <T> void insert(String strTableName, List<T> listEntity) {
    SQLiteDatabase objSQLiteDatabase = INSTANCE.getWritableDatabase();
    objSQLiteDatabase.beginTransaction();
    try {
      ContentValues objContentValues = new ContentValues();
      for (T objEntity : listEntity) {
        INSTANCE.itfCallBack.assignValuesByEntity(strTableName, objEntity, objContentValues);
        objSQLiteDatabase.insert(strTableName, null, objContentValues);
        objContentValues.clear();
      }
      objSQLiteDatabase.setTransactionSuccessful();
    } finally {
      objSQLiteDatabase.endTransaction();
      objSQLiteDatabase.close();
    }
  }

  public static <T> void update(String strTableName, T objEntity, String strWhereClause, String[] aryWhereArgs) {
    SQLiteDatabase objSQLiteDatabase = INSTANCE.getWritableDatabase();
    objSQLiteDatabase.beginTransaction();
    try {
      ContentValues objContentValues = new ContentValues();
      INSTANCE.itfCallBack.assignValuesByEntity(strTableName, objEntity, objContentValues);
      objSQLiteDatabase.update(strTableName, objContentValues, strWhereClause, aryWhereArgs);
      objContentValues.clear();
      objSQLiteDatabase.setTransactionSuccessful();
    } finally {
      objSQLiteDatabase.endTransaction();
      objSQLiteDatabase.close();
    }
  }

  public static <T> List<T> query(String strTableName, @NonNull String strQuery, @Nullable String[] aryWhereArgs) {
    SQLiteDatabase objSQLiteDatabase = INSTANCE.getReadableDatabase();
    Cursor objCursor = objSQLiteDatabase.rawQuery(strQuery, aryWhereArgs);
    try {
      List<T> listReturn = new ArrayList<>(objCursor.getCount());
      if (objCursor.moveToFirst()) {
        do {
          T objEntity = INSTANCE.itfCallBack.newEntityByCursor(strTableName, objCursor);
          if (objEntity != null) {
            listReturn.add(objEntity);
          }
        } while (objCursor.moveToNext());
      }
      return listReturn;
    } finally {
      objCursor.close();
      objSQLiteDatabase.close();
    }
  }

  public static void deleteAllDataFromTable(String strTableName) {
    SQLiteDatabase objSQLiteDatabase = INSTANCE.getWritableDatabase();
    objSQLiteDatabase.beginTransaction();
    try {
      String strSql = "DELETE FROM " + strTableName;
      objSQLiteDatabase.execSQL(strSql);
      objSQLiteDatabase.setTransactionSuccessful();
    } finally {
      objSQLiteDatabase.endTransaction();
      objSQLiteDatabase.close();
    }
  }

  public static void deleteByWhere(String strTableName, String strWhereClause, String[] aryWhereArgs) {
    SQLiteDatabase objSQLiteDatabase = INSTANCE.getWritableDatabase();
    objSQLiteDatabase.beginTransaction();
    try {
      objSQLiteDatabase.delete(strTableName, strWhereClause, aryWhereArgs);
      objSQLiteDatabase.setTransactionSuccessful();
    } finally {
      objSQLiteDatabase.endTransaction();
      objSQLiteDatabase.close();
    }
  }

  public static void execSQL(String strSql) {
    SQLiteDatabase objSQLiteDatabase = INSTANCE.getWritableDatabase();
    objSQLiteDatabase.beginTransaction();
    try {
      objSQLiteDatabase.execSQL(strSql);
      objSQLiteDatabase.setTransactionSuccessful();
    } finally {
      objSQLiteDatabase.endTransaction();
      objSQLiteDatabase.close();
    }
  }

}
